#import <LoadCommand.h>
/*
 * $Log:	LoadCommand.m,v $
Revision 1.12  94/05/28  16:17:05  ediger
added LoadCommand -getUpperAddress and - (struct load_command *)loadCommandAddress

Revision 1.11  94/01/30  16:25:39  ediger
added working version of -(unsigned long)pc method

Revision 1.10  93/12/28  23:09:05  ediger
removed printfs for memory leak checking.

Revision 1.9  93/12/28  23:07:40  ediger
Fixed memory leak caused by sections instance var of SegmentCommand
subclass not getting freed.  overrode -free method for that subclass.
Left in the debug statements

Revision 1.8  93/12/08  00:52:12  ediger
added some assert()s in accordance with new ideology

Revision 1.7  93/12/02  00:45:36  ediger
fixed a renamed method (initFromFile: becomes initFromFileNamed:)

Revision 1.6  93/10/31  21:36:40  ediger
changed initialization of new LoadCommand object from 'new'
to 'alloc] init]'.

Revision 1.3  93/10/28  00:07:08  ediger
correct a few typos, add better commentary

Revision 1.2  93/10/27  23:43:04  ediger
changed to one subclass per type of load command

 */

@implementation LoadCommand

static char rcsident[] = "@(#) $Id: LoadCommand.m,v 1.12 94/05/28 16:17:05 ediger Exp Locker: ediger $";

//M+	LoadCommand +new
//M-
+ new
{
	LoadCommand *oNew = [[LoadCommand alloc] init];
	assert(oNew != NULL);
	return oNew;
}

//M+	LoadCommand +new:
//		Accounts for cheesy polymorphism of Mach-O load commands.
//		Each of them begins with a struct load_command, but
//		a variable amount of stuff follows (and subsumes)
//		each struct load_command.
//M-
+ new: (caddr_t)loadCommandAddress;
{
	id oNew = Nil;
	struct load_command *lc = (struct load_command *)loadCommandAddress;

	assert(NULL != loadCommandAddress);

	switch (lc->cmd)
	{
	case LC_SEGMENT:
		oNew = [[SegmentCommand alloc] init];
		break;
	case LC_IDFVMLIB:
		oNew = [[IdFVMLibCommand alloc] init];
		break;
	case LC_LOADFVMLIB:
		oNew = [[LoadFVMLibCommand alloc] init];
		break;
	case LC_FVMFILE:
		oNew = [[FVMFileCommand alloc] init];
		break;
	case LC_SYMTAB:
		oNew = [[SymTabCommand alloc] init];
		break;
	case LC_SYMSEG:
		oNew = [[SymSegCommand alloc] init];
		break;
	case LC_THREAD:
		oNew = [[ThreadCommand alloc] init];
		break;
	case LC_UNIXTHREAD:
		oNew = [[UnixThreadCommand alloc] init];
		break;
	case LC_IDENT:
		oNew = [[IdentCommand alloc] init];
		break;
	default:
		oNew = [[LoadCommand alloc] init];
		break;
	}

	assert(Nil != oNew);

	[oNew setLoadCommand:loadCommandAddress];

	if (lc->cmd == LC_SEGMENT)
		[oNew fillSections];

	return oNew;
}

//M+	LoadCommand -init
// Assumes that "self" is already allocated.
//M-
- init
{
	[super init];
	loadCommand = NULL;
	mapped = FALSE;
	otherFile = FALSE;
	return self;
}

//M+	LoadCommand -free
//M-
- free
{
	[super free];
	return self;
}

//M+	LoadCommand -setSegmentCommand
//M-
- setLoadCommand: (caddr_t)loadCommandAddress;
{
	assert(loadCommandAddress != NULL);
	loadCommand = (struct load_command *)loadCommandAddress;
	return self;
}

//M+	LoadCommand -isMapped
//		Does the object represent a memory-mapped piece
//		of the file?  Probably not.
//M-
- (BOOL)isMapped
{
	return mapped;
}

//M+	LoadCommand -isThread
//		Does the object represent a thread of some sort.
//		of the file?  Again, Probably not.
//M-
- (BOOL)isThread
{
	return FALSE;
}

//M+	LoadCommand -representsMappedFile
//		Does the object represent a load command
//		that would end up causing some other file
//		to be memory-mapped into the task's address
//		space?
//M-
- (BOOL)representsMappedFile
{
	return otherFile;
}


//M+	LoadCommand -getBaseAddress
//M-
- (unsigned long)getBaseAddress
{
	return -1;
}

//M+	LoadCommand -getUpperAddress
//M-
- (unsigned long)getUpperAddress
{
	return -1;
}

//M+	LoadCommand -(unsigned long)commandSize
//M-
- (unsigned long)commandSize
{
	return loadCommand->cmdsize;
}

//M+	LoadCommand -(char *)commandName
//M-
- (char *)commandName
{
	return "load command";
}

//M+	LoadCommand - (int)numberOfSections
//		Only struct segment_commands have sections,
//		but it's handy to use this as a flag.
//M-
- (int)numberOfSections
{
	return 0;
}

//M+	LoadCommand -struct load_command *)loadCommandAddress
//M-
- (struct load_command *)loadCommandAddress
{
	return loadCommand;
}

@end


@implementation SegmentCommand

//M+	SegmentCommand -init
//M-
- init
{
	[super init];
	sections       = NULL;
	segmentCommand = NULL;
	mapped         = TRUE;  // struct segment_commands do map
	return self;
}

//M+	SegmentCommand -free
//		Had to override so as to free sections instance var
//M-
- free
{
	if (NULL != sections)
		free(sections);
	[super free];
	return self;
}

//M+	SegmentCommand -fillSections
//		Create a quick-to-access array of pointers to
//		the struct sections in this load command.
//M-
- fillSections
{
	assert(segmentCommand != NULL);
	sections
		 = (struct section **)malloc(
			sizeof(struct section)*segmentCommand->nsects);

	if (sections != NULL)
	{	int iCC;
		struct section *spSection
			 = (struct section *)((unsigned long)segmentCommand
				 + sizeof(struct segment_command));

		for (iCC = 0; iCC < segmentCommand->nsects; ++iCC)
			sections[iCC] = spSection++;
	}

	return self;
}

//M+	SegmentCommand -getBaseAddress
//M-
- (unsigned long)getBaseAddress
{
	assert(segmentCommand != NULL);
	return segmentCommand->vmaddr;
}

//M+	SegmentCommand -getUpperAddress
//M-
- (unsigned long)getUpperAddress
{
	assert(segmentCommand != NULL);
	return segmentCommand->vmaddr + segmentCommand->vmsize;
}

//M+	SegmentCommand -setLoadCommand
//M-
- setLoadCommand: (caddr_t)loadCommandAddress;
{
	assert(loadCommandAddress != NULL);
	[super setLoadCommand: loadCommandAddress];
	segmentCommand = (struct segment_command *)loadCommandAddress;
	return self;
}


//M+	SegmentCommand -(char *)commandName
//M-
- (char *)commandName
{
	assert(segmentCommand != NULL);
	if (segmentCommand->segname == NULL || strlen(segmentCommand->segname) <= 0)
		return "LC_SEGMENT";  // relocatable object files?
	else
		return segmentCommand->segname;
}

//M+	SegmentCommand -numberOfSections
//M-
- (int)numberOfSections
{
	assert(segmentCommand != NULL);
	return segmentCommand->nsects;
}


//M+	SegmentCommand -(struct section *)getSection:(int)sectionNumber
//M-
- (struct section *)getSection:(int)sectionNumber
{
	assert(segmentCommand != NULL);
	if (sections != NULL && sectionNumber < segmentCommand->nsects)
		return sections[sectionNumber];

	return NULL;
}

@end

@implementation LoadFVMLibCommand

//M+	LoadFVMLibCommand -init
//M-
- init
{
	[super init];
	fvmlibCommand = NULL;
	mapped = FALSE;
	otherFile = TRUE;
	theOtherFile = Nil;
	return self;
}

//M+	LoadFVMLibCommand -getBaseAddress
//M-
- (unsigned long)getBaseAddress
{
	assert(fvmlibCommand != NULL);
	return fvmlibCommand->fvmlib.header_addr;
}

//M+	LoadFVMLibCommand -setLoadCommand
//M-
- setLoadCommand: (caddr_t)loadCommandAddress;
{
	assert(loadCommandAddress != NULL);
	[super setLoadCommand: loadCommandAddress];
	fvmlibCommand = (struct fvmlib_command *)loadCommandAddress;
	return self;
}

//M+	LoadFVMLibCommand -(char *)commandName
//M-
- (char *)commandName
{
	assert(fvmlibCommand != NULL);
	return (char *)((unsigned long)fvmlibCommand
            + fvmlibCommand->fvmlib.name.offset);
}

//M+	LoadFVMLibCommand -loadOtherFile
//M-
- loadOtherFile
{
	if (fvmlibCommand != NULL)
	{	theOtherFile = [[MachOFile alloc] init];
		[theOtherFile fillFromFileNamed:[self commandName]];
	}

	return self;
}

//M+	LoadFVMLibCommand -otherFile
//M-
- otherFile
{
	return theOtherFile;
}

//M+	LoadFVMLibCommand -free
//		Had to override this to ditch any other mapped files. 
//M-
- free
{
	if (theOtherFile != Nil)
		[theOtherFile free];

	[super free];

	return self;
}

@end

@implementation IdFVMLibCommand

//M+	IdFVMLibCommand -init
//M-
- init
{
	[super init];
	namebuf = NULL;
	mapped = FALSE;
	return self;
}

//M+	IdFVMLibCommand -commandName
//		This is cheesy, but it separates the ident sections from
//		the mapped sections.
//M-
- (char *)commandName
{
	char *filename;
#define IDENT_PHRASE "FVM Lib ident: "

	if (namebuf != NULL)
		free(namebuf);

	filename = [super commandName];

	namebuf = malloc(strlen(filename) + strlen(IDENT_PHRASE) + 1);

	if (namebuf != NULL)
		strcat(strcpy(namebuf, IDENT_PHRASE), filename);

	return namebuf;
}

//M+	IdFVMLibCommand -free
//		Possibly this object has allocated some memory for its
//		own purposes.  Free it.
//M-
- free
{
	if (namebuf != NULL)
		free(namebuf);

	[super free];

	return self;
}

@end

@implementation FVMFileCommand

//M+	FVMFileCommand -init
/*
 * The fvmfile_command contains a reference to a file to be loaded at the
 * specified virtual address.  (Presently, this command is reserved for NeXT
 * internal use.  The kernel ignores this command when loading a program into
 * memory).
 */
//M-
- init
{
	[super init];
	mapped = TRUE;
	return self;
}

//M+	FVMFileCommand -getBaseAddress
//M-
- (unsigned long)getBaseAddress
{
	assert(fvmFileCommand != NULL);
	return fvmFileCommand->header_addr;  // address of FVM file's header
}

//M+	FVMFileCommand -setLoadCommand
//M-
- setLoadCommand: (caddr_t)loadCommandAddress;
{
	assert(loadCommandAddress != NULL);
	[super setLoadCommand: loadCommandAddress];
	fvmFileCommand = (struct fvmfile_command *)loadCommandAddress;
	return self;
}

//M+	FVMFileCommand -(char *)commandName
//M-
- (char *)commandName
{
	assert(fvmFileCommand != NULL);
	return (char *)((unsigned long)fvmFileCommand
            + fvmFileCommand->name.offset);
}

@end

@implementation SymTabCommand

//M+	SymTabCommand -init
//M-
- init
{
	[super init];
	mapped = FALSE;
	return self;
}

//M+	SymTabCommand -setLoadCommand
//M-
- setLoadCommand: (caddr_t)loadCommandAddress;
{
	[super setLoadCommand: loadCommandAddress];
	symTabCommand = (struct symtab_command *)loadCommandAddress;
	return self;
}

//M+	SymTabCommand -(char *)commandName
//M-
- (char *)commandName
{
	return "LC_SYMTAB";
}
@end

@implementation SymSegCommand

//M+	SymSegCommand -init
//M-
- init
{
	[super init];
	mapped = FALSE;
	return self;
}

//M+	SymSegCommand -setLoadCommand
//M-
- setLoadCommand: (caddr_t)loadCommandAddress;
{
	assert(loadCommandAddress != NULL);
	[super setLoadCommand: loadCommandAddress];
	symSegCommand = (struct symseg_command *)loadCommandAddress;
	return self;
}

//M+	SymSegCommand -(char *)commandName
//M-
- (char *)commandName
{
	return "LC_SYMSEG";
}
@end

@implementation ThreadCommand
//M+	ThreadCommand +new

//M+	ThreadCommand -init
//M-
- init
{
	[super init];
	mapped = FALSE;
	return self;
}

//M+	ThreadCommand -setLoadCommand
//M-
- setLoadCommand: (caddr_t)loadCommandAddress;
{
	assert(loadCommandAddress != NULL);
	[super setLoadCommand: loadCommandAddress];
	threadCommand = (struct thread_command *)loadCommandAddress;
	return self;
}

//M+	ThreadCommand -(char *)commandName
//M-
- (char *)commandName
{
	return "LC_THREAD";
}

//M+	ThreadCommand -(BOOL)isThread
//M-
- (BOOL)isThread
{
	return TRUE;
}

- (unsigned long)pc
{
	struct thread_state_flavor *spThreadStateFlavor;
	unsigned long               lNextAddress;
	unsigned int irPC = -1;

	spThreadStateFlavor =
	 (struct thread_state_flavor *)((unsigned long)threadCommand
		+ sizeof(struct thread_command));

	lNextAddress = (unsigned long)spThreadStateFlavor
		+ sizeof(struct thread_state_flavor);

	switch(spThreadStateFlavor->flavor)
	{
	case M68K_THREAD_STATE_REGS:
		irPC = ((struct m68k_thread_state_regs *)lNextAddress)->pc;
		break;
	case M68K_THREAD_STATE_68882:
		irPC = ((struct m68k_thread_state_68882 *)lNextAddress)->iar;
		break;
	case M68K_THREAD_STATE_USER_REG:
		irPC = ((struct m68k_thread_state_user_reg *)lNextAddress)->user_reg;
		break;
	case i386_THREAD_STATE:
		irPC = ((i386_thread_state_t *)lNextAddress)->eip;
		break;
	}
	 
	return irPC;
}

@end

@implementation UnixThreadCommand

//M+	UnixThreadCommand -(char *)commandName
//M-
- (char *)commandName
{
	return "LC_UNIXTHREAD";
}

@end

@implementation IdentCommand

//M+	IdentCommand -(char *)commandName
//M-
- (char *)commandName
{
	return "LC_IDENT";
}
@end
